/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.j4sc.submission;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.stream.XMLStreamException;

import org.apache.commons.lang.StringUtils;

import com.googlecode.j4sc.PartyInformation;
import com.googlecode.j4sc.UserCredentials;
import com.googlecode.j4sc.service.Service;
import com.googlecode.j4sc.submission.audio.Audio;

/**
 * The Submission class represents a submission to the SpinVox Create Service
 * using the {@link Service#submit(Submission)} method
 * <p>
 * A submission is the audio to be converted and associated meta-data.
 * 
 * @author Raj Patel, Dariusz Lorenc
 */
public class Submission {
	
	private static final Pattern PATTERN = Pattern.compile("\\d{4}-\\d{4}-\\d{4}-\\d{4}");
	
	private final String accountId;
	private final String applicationName;
	private final String reference;
	private final PartyInformation partyInformation;
	private final Audio audio;
	private final UserCredentials userCredentials;
	
	/**
	 * Construct a new submission
	 * 
	 * @param accountId
	 *            The account-id number provided by SpinVox on your My Account
	 *            page. Is a 16 digit number in 4 four digit blocks delimited by
	 *            hyphens (e.g. 1234-1234-1234-1234)
	 * @param applicationName
	 *            the application name
	 * @param reference
	 *            a unique reference for this submission
	 * @param audio
	 *            the audio to be submitted for conversion
	 * @param userCredentials
	 *            the user credentials for your account
	 * @throws IllegalArgumentException 
	 * 			  thrown if invalid values are provided
	 */
	public Submission(String accountId, String applicationName, String reference, Audio audio, 
			UserCredentials userCredentials) throws IllegalArgumentException {
		this(accountId, applicationName, reference, audio, null, userCredentials);
	}

	/**
	 * Construct a new submission
	 * 
	 * @param accountId
	 *            The account-id number provided by SpinVox on your My Account
	 *            page. Is a 16 digit number in 4 four digit blocks delimited by
	 *            hyphens (e.g. 1234-1234-1234-1234)
	 * @param applicationName
	 *            the application name
	 * @param reference
	 *            a unique reference for this submission
	 * @param audio
	 *            the audio to be submitted for conversion
	 * @param partyInformation
	 *            the party information
	 * @param userCredentials
	 *            the user credentials for your account
	 * @throws IllegalArgumentException 
	 * 			  thrown if invalid values are provided
	 */
	public Submission(String accountId, String applicationName, String reference, Audio audio, 
			PartyInformation partyInformation, UserCredentials userCredentials) 
			throws IllegalArgumentException {
		
		enforceNotEmptyString(accountId, "Account ID");
		enforceNotEmptyString(reference, "Reference");
		enforceNotEmptyString(applicationName, "Application name");
		enforceNotNull(audio, "Audio");
		enforceNotNull(userCredentials, "User Credentials");
		validateAccountId(accountId);
		
		this.userCredentials = userCredentials;
		this.accountId = accountId;
		this.applicationName = applicationName;
		this.reference = reference;
		this.audio = audio;
		this.partyInformation = partyInformation;
	}
	
	/**
	 * Creates the multipart MIME body of the submission HTTP request. As such,
	 * it is not intented to be used by API end users
	 * 
	 * @return the multipart MIME body
	 */
	public String createMimeBody() {
		String xmlPayload;
		try {
			xmlPayload = SubmissionMetaData.createXmlMetadata(accountId, applicationName, reference, partyInformation);
		} catch (XMLStreamException e) {
			throw new InternalException(e);
		}
		return new MultiPartMimeBodyBuilder()
			.addPart()
			.withHeader(MultiPartMimeBodyBuilder.Header.CONTENT_TYPE, "text/xml")
			.withContent(xmlPayload)
			.addPart()
			.withHeader(MultiPartMimeBodyBuilder.Header.CONTENT_TYPE, "audio/wav")
			.withHeader(MultiPartMimeBodyBuilder.Header.CONTENT_TRANSFER_ENCODING, "base64")
			.withContent(audio.toBase64())
			.toBody();		
	}

	/**
	 * Get the user credentials to be used in the submission
	 * 
	 * @return the user credentials
	 */
	public UserCredentials getUserCredentials() {
		return this.userCredentials;
	}

	private static void enforceNotEmptyString(String value, String fieldName) {
		if (StringUtils.isBlank(value)) {
			throw new IllegalArgumentException(fieldName + " must not be null or blank");
		}
	}

	private static void enforceNotNull(Object object, String fieldName) {
		if (object == null) {
			throw new IllegalArgumentException(fieldName + " must not be null");
		}
	}
	
	private void validateAccountId(String accountId) {
		Matcher matcher = PATTERN.matcher(accountId);
		if(!matcher.matches()) {
			throw new IllegalArgumentException(
					String.format("Account ID [%s] is invalid, must be 16 digits broken up into 4 digit parts " +
							"separated by hyphens e.g. '1234-1234-1234-1234'", accountId));
		}
	}
}
